package com.econage.eccm.test.base.mybatis.adaptation;

import com.econage.eccm.test.base.mybatis.enums.DBType;
import com.econage.eccm.test.base.mybatis.plugins.pagination.PaginationInterceptor;
import com.econage.eccm.test.base.mybatis.support.WebTransactionFactory;
import com.econage.eccm.test.base.mybatis.uid.dbincrementer.DB2KeyGenerator;
import com.econage.eccm.test.base.mybatis.uid.dbincrementer.H2KeyGenerator;
import com.econage.eccm.test.base.mybatis.uid.dbincrementer.OracleKeyGenerator;
import com.econage.eccm.test.base.mybatis.uid.dbincrementer.PostgreKeyGenerator;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.reflect.Reflection;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.logging.log4j.Log4jImpl;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.JdbcType;

import javax.sql.DataSource;
import java.io.Reader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Properties;

public class MybatisXMLConfigBuilder {

    private boolean parsed;
    private MybatisConfiguration configuration;
    private MybatisGlobalAssistant globalAssistant;
    private XPathParser parser;

    public MybatisXMLConfigBuilder(Reader reader){
        this(new XPathParser(reader, false, null, new XMLMapperEntityResolver()));
    }
    public MybatisXMLConfigBuilder(XPathParser parser) {
        this.parser = parser;
        this.configuration = new MybatisConfiguration();
        this.globalAssistant = new MybatisGlobalAssistant(configuration);
        this.configuration.setGlobalAssistant(globalAssistant);
    }


    public void parse() {
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;

        trivialSet();
        parseConfiguration(parser.evalNode("/configuration"));
    }

    private void trivialSet(){
        /*--------------若干固定配置*/
        TransactionFactory transactionFactory = new WebTransactionFactory();
        Environment environment = new Environment("produce", transactionFactory, emptyDataSource());
        configuration.setEnvironment(environment);
        //禁止懒加载，服务器api化后，意义不大
        configuration.setLazyLoadingEnabled(false);
        configuration.setAggressiveLazyLoading(false);
        //禁止多结果
        configuration.setMultipleResultSetsEnabled(false);
        configuration.setDefaultExecutorType(ExecutorType.SIMPLE);
        configuration.setMapUnderscoreToCamelCase(true);
        //禁用二级缓存
        configuration.setCacheEnabled(false);
        configuration.setLogImpl(Log4jImpl.class);
        /*--------------若干固定配置--------------*/
        configuration.addInterceptor(new PaginationInterceptor());
    }

    private void parseConfiguration(XNode root) {
        parseSetting(root.evalNode("settings"));
        parseCache(root.evalNode("cache"));
        parseBasePackages(root.evalNode("basePackages"));
    }

    private void parseSetting(XNode parent){
        Properties props = parent.getChildrenAsProperties();
        globalAssistant.setDbType(props.getProperty("dbType"));

        if(globalAssistant.getDbType().equals(DBType.ORACLE)){
            //oracle特殊设置
            configuration.setJdbcTypeForNull(JdbcType.NULL);
            globalAssistant.setKeyGenerator(new OracleKeyGenerator());
        }else if(globalAssistant.getDbType().equals(DBType.H2)){
            globalAssistant.setKeyGenerator(new H2KeyGenerator());
        }else if(globalAssistant.getDbType().equals(DBType.POSTGRE)){
            globalAssistant.setKeyGenerator(new PostgreKeyGenerator());
        }else if(globalAssistant.getDbType().equals(DBType.DB2)){
            globalAssistant.setKeyGenerator(new DB2KeyGenerator());
        }

        globalAssistant.setMapperNameValidate(booleanValueOf(props.getProperty("mapperNameValidate"),false));
    }

    private void parseCache(XNode cacheNode){
        if(cacheNode!=null){
            /*MybatisCacheAssistant cacheAssistant = new MybatisCacheAssistant();
            cacheAssistant.configCacheWithRemoteInfo(cacheNode);
            globalAssistant.setMybatisCacheAssistant(cacheAssistant);*/

            /*
            XNode remoteCacheNode = cacheNode.evalNode("remote");
            if(remoteCacheNode==null){
                cacheAssistant.configCacheWithRemoteInfo(null);
            }else{
                cacheAssistant.configCacheWithRemoteInfo(remoteCacheNode.getChildrenAsProperties());
            }*/
        }
    }


    private void parseBasePackages(XNode parent){
        List<String> packageNames = Lists.newArrayList();
        for(XNode child : parent.getChildren()){
            packageNames.add(child.getStringAttribute("name"));
        }
        globalAssistant.scanMapper(packageNames.toArray(new String[0]));
    }

    private Boolean booleanValueOf(String value, Boolean defaultValue) {
        return value == null ? defaultValue : Boolean.valueOf(value);
    }


    public MybatisConfiguration getConfiguration() {
        return configuration;
    }

    public MybatisGlobalAssistant getGlobalAssistant() {
        return globalAssistant;
    }

    public DataSource emptyDataSource(){
        //空白的DataSource,不做任何事
        return Reflection.newProxy(DataSource.class, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        });
    }
}
